'''
Created on 2018年1月14日

@author: heguofeng
'''
import os
from src.file_folder import FolderInfo, NODE_TYPE_FOLDER, NODE_TYPE_FILE, FolderWidgets, FileInfo, SIZE_FILTER_TUPLE

from tkinter import *
from tkinter.ttk import *
from tkinter.filedialog import *
from PIL import Image, ImageTk
from _stat import S_ISDIR, S_ISREG

import shutil
import unittest

FILE_STATUS_UNKNOWN = 128
FILE_STATUS_NEW = 1
FILE_STATUS_OLD = 2 
FILE_STATUS_SAME = 4
STATUS_STRING = {
                FILE_STATUS_UNKNOWN:"?", \
                FILE_STATUS_NEW:">", \
                FILE_STATUS_OLD:"<", \
                FILE_STATUS_SAME:"=" }


class SyncFolderInfo(FolderInfo):

    def __init__(self, folder_path, folder_name=""):
        super().__init__(folder_path, folder_name)
        self.compareResult = {}
        
    def load(self, folder_path, folder_name=""):
        self.path = os.path.join(folder_path, folder_name)
        self.name = folder_name
        '''get files in path
           if folder then creat FileFolder
        '''
        full_path = self.path
        if full_path == "":
            return 
        for f in os.listdir(full_path):
            f_full_path = os.path.join(full_path, f)
            try:
                stat_result = os.stat(f_full_path)
                if S_ISREG(stat_result.st_mode):
                    self.nodes[f] = FileInfo(full_path, f, stat_result)
                if S_ISDIR(stat_result.st_mode):
                    self.nodes[f] = SyncFolderInfo(full_path, f)
            except:
                pass
            
    def compare(self, other):    
        # self.compareResult = super().compare(other)

        for n in self.nodes:
            node = self.nodes[n]
            otherNode = other.nodes.get(n, None) if other != None else None
            if isinstance(node, FileInfo):
                otherNode = otherNode if isinstance(otherNode, FileInfo) else None
                self.compareResult[n] = node.compare(otherNode)
            elif isinstance(node, FolderInfo):
                self.compareResult[n] = node.compare(otherNode)
        isSameOld = FILE_STATUS_SAME
        for n in self.nodes:
            if self.compareResult[n] == FILE_STATUS_NEW:
                return FILE_STATUS_NEW
            if self.compareResult[n] == FILE_STATUS_OLD:
                isSameOld = FILE_STATUS_OLD
        return isSameOld
        
    def synchronize(self, dest_folder_info):
        if dest_folder_info == None:
            return
        for node in self.nodes:
            nodeInfo = self.nodes[node]
            nodeResult = self.compareResult[node]
            if isinstance(nodeInfo, FileInfo):
                if nodeResult == FILE_STATUS_NEW or nodeResult == FILE_STATUS_UNKNOWN:
                    shutil.copy2(os.path.join(nodeInfo.path, nodeInfo.name), \
                                os.path.join(dest_folder_info.path, nodeInfo.name))
                    print("source %s / %s copyto dest %s" % (nodeInfo.path, nodeInfo.name, dest_folder_info.path))
            elif isinstance(nodeInfo, SyncFolderInfo):
                destFolderInfo = dest_folder_info.nodes.get(node, None)
                if destFolderInfo == None:
                    newDstDir = os.path.join(dest_folder_info.path, nodeInfo.name)
                    print(" dest %s / %s create" % (dest_folder_info.path, nodeInfo.name))
                    os.mkdir(newDstDir)
                    destFolderInfo = SyncFolderInfo(newDstDir, "")
                    dest_folder_info.nodes[nodeInfo.name] = destFolderInfo
                nodeInfo.synchronize(destFolderInfo)
 

class FileSynchronizer(object):

    def __init__(self, sourcepath, destinationpath):
        self.srcDir = sourcepath
        self.dstDir = destinationpath
        self.srcFolderInfo = SyncFolderInfo(self.srcDir)
        self.dstFolderInfo = SyncFolderInfo(self.dstDir)

        pass
    
    def move(self):
        pass
    
    def compare(self):
        self.srcFolderInfo.compare(self.dstFolderInfo)
        self.dstFolderInfo.compare(self.srcFolderInfo)
        pass
    
    def sync(self):
        self.compare()
        self.srcFolderInfo.synchronize(self.dstFolderInfo)
    
    def dst2srcSync(self):
        self.compare()
        self.dstFolderInfo.synchronize(self.srcFolderInfo)
        
    @staticmethod
    def reverseStatus(status):
        if status == FILE_STATUS_NEW:
            return FILE_STATUS_OLD
        if status == FILE_STATUS_OLD:
            return FILE_STATUS_NEW
        return status


class CompareWidgets(Frame):

    def __init__(self, master=None, cmpFun=None):
        super().__init__(master)
        self.compare = cmpFun
        self.createWidgets()
        self.iid2srcIid = {}
    
    def createWidgets(self):

        self.compareButton = Button(self, text="比较", command=self.compare)
        self.compareButton.grid(row=0, column=0)
        self.compareTreeview = Treeview(self, columns=("Status"), \
                                displaycolumns=["Status"], show='headings', height=16)
        self.compareTreeview.column('Status', width=20)
        self.compareTreeview.grid(row=1, column=0, rowspan=16, sticky=E + W)
        self.compareScrollbar = Scrollbar(self, orient=VERTICAL)
        self.compareScrollbar.grid(row=1, column=0, rowspan=16, sticky=N + S + E)
        
        self.compareScrollbar.configure(command=self.compareTreeview.yview)
        self.compareTreeview.configure(yscrollcommand=self.compareScrollbar.set)
        self.statusFilterCombobox = Combobox(self, \
                                             values=("All", ">", '=', "<", '?'), \
                                             width=6, \
                                             )
        self.statusFilterCombobox.set("All")
        self.statusFilterCombobox.grid(row=17, column=0, sticky=S)        
        
    def clearTreeview(self):
        for i in self.compareTreeview.get_children():
            self.compareTreeview.delete(i)
#         self.statusFilterCombobox.set("All")
            
    def insertTreeviewItem(self, fileinfo, status):
        if fileinfo == None or isinstance(fileinfo, SyncFolderInfo):
            iid = self.compareTreeview.insert(parent="", index='end', values=(""))
            return iid
        if isinstance(fileinfo, FileInfo):
            iid = self.compareTreeview.insert(parent="", index='end', \
                                 values=(STATUS_STRING[status]))
        return iid     
    
    def deleteTreeviewItem(self):
        pass        
    
    def refreshTreeview(self):
        pass
    
            
class SynchronizeWidgets(Frame):
    '''
    widgets has directory label,directory entry,open file dialog button and directory view
    also has popupmenu 
    select event 
    '''

    def __init__(self, master=None, config={}):
        super().__init__(master)
        self.config = config
        self.create_widgets()
        self.compareWidgets.statusFilterCombobox.bind("<<ComboboxSelected>>", self.filtersyncShow)
        self.srcFolderWidgets.sizeFilterCombobox.bind("<<ComboboxSelected>>", self.sizeDateFilterSyncShow)
        self.srcFolderWidgets.dateFilterCombobox.bind("<<ComboboxSelected>>", self.sizeDateFilterSyncShow)
        self.dstFolderWidgets.sizeFilterCombobox.bind("<<ComboboxSelected>>", self.sizeDateFilterSyncShow)
        self.dstFolderWidgets.dateFilterCombobox.bind("<<ComboboxSelected>>", self.sizeDateFilterSyncShow)
        self.srcIidDstIidDict = {}
        
    def create_widgets(self):

        self.srcFolderWidgets = FolderWidgets(self, folder_label="源目录", show_full_path=False)
        self.compareWidgets = CompareWidgets(self, self.compare)
        self.dstFolderWidgets = FolderWidgets(self, folder_label="目标目录")

        self.srcFolderWidgets.syncButton = Button(self.srcFolderWidgets, text="同步", command=self.doSync)
        self.dstFolderWidgets.syncButton = Button(self.dstFolderWidgets, text="向左同步", command=self.doDst2SrcSync)
        
        self.srcFolderWidgets.folderPath.set(self.config.get("last_source_path", "."))
        self.dstFolderWidgets.folderPath.set(self.config.get("last_destination_path", "."))
        
        self.srcFolderWidgets.grid(row=0, column=0, columnspan=5, rowspan=18, sticky=N + S + E + W)
        self.compareWidgets.grid(row=0, column=5, rowspan=17, sticky=N + S + E + W)        
        self.dstFolderWidgets.grid(row=0, column=6, columnspan=5, rowspan=18, sticky=N + S + E + W)
        
        self.srcFolderWidgets.syncButton.grid(row=17, column=4)
        self.dstFolderWidgets.syncButton.grid(row=17, column=4)        
        
        self.srcFolderWidgets.columnconfigure(0, weight=5)    
        self.compareWidgets.columnconfigure(0, weight=1)
        self.dstFolderWidgets.columnconfigure(0, weight=5)   
                
        self.srcFolderWidgets.dirScrollbar.configure(command=self.syncScrollbar)
        self.dstFolderWidgets.dirScrollbar.configure(command=self.syncScrollbar)
        self.compareWidgets.compareScrollbar.configure(command=self.syncScrollbar)

    def syncScrollbar(self, mtype, howmuch, unit=""):
        if mtype == "moveto":
            self.srcFolderWidgets.dirTreeview.yview(mtype, howmuch)
            self.dstFolderWidgets.dirTreeview.yview(mtype, howmuch)
            self.compareWidgets.compareTreeview.yview(mtype, howmuch)
        else:
            self.srcFolderWidgets.dirTreeview.yview(mtype, howmuch, unit)
            self.dstFolderWidgets.dirTreeview.yview(mtype, howmuch, unit)
            self.compareWidgets.compareTreeview.yview(mtype, howmuch, unit)
       
        pass
    
    def filtersyncShow(self, event):
        status = self.compareWidgets.statusFilterCombobox.get()
        self.srcFolderWidgets.clearTreeview()
        self.dstFolderWidgets.clearTreeview()
        self.compareWidgets.clearTreeview()
        self.syncShowStatus2(self.fileSyncer.srcFolderInfo, self.fileSyncer.dstFolderInfo, "", "", status)
        self.refreshStatusShow()
        
    def sizeDateFilterSyncShow(self, event):
        status = self.compareWidgets.statusFilterCombobox.get()

        if event.widget == self.srcFolderWidgets.sizeFilterCombobox or event.widget == self.dstFolderWidgets.sizeFilterCombobox:
            s = event.widget.get()
            self.srcFolderWidgets.sizeFilterCombobox.set(s)
            self.dstFolderWidgets.sizeFilterCombobox.set(s)
        if event.widget == self.srcFolderWidgets.dateFilterCombobox or event.widget == self.dstFolderWidgets.dateFilterCombobox:
            s = event.widget.get()
            self.srcFolderWidgets.dateFilterCombobox.set(s)
            self.dstFolderWidgets.dateFilterCombobox.set(s)

        self.srcFolderWidgets.clearTreeview()
        self.dstFolderWidgets.clearTreeview()
        self.compareWidgets.clearTreeview()        
        self.syncShowStatus2(self.fileSyncer.srcFolderInfo, self.fileSyncer.dstFolderInfo, "", "", status)
        self.refreshStatusShow()
        pass
    
    def refreshStatusShow(self):
        self.compareWidgets.clearTreeview()
        self.showStatus(self.fileSyncer.srcFolderInfo, "")
        
    def showStatus(self, folderInfo, parentiid):
        for i in self.srcFolderWidgets.dirTreeview.get_children(parentiid):
            nodeInfo = self.srcFolderWidgets.iidNodeInfoDict[i]
            if nodeInfo == None :
                # get dst iid
                dstIid = self.srcIidDstIidDict[i]
                dstnodeInfo = self.dstFolderWidgets.iidNodeInfoDict[dstIid]
                status = FILE_STATUS_OLD
                statusiid = self.compareWidgets.insertTreeviewItem(dstnodeInfo, status)
                self.showStatus(None, i)
            if isinstance(nodeInfo, FileInfo):
                # get from current folder
                # print(folderInfo.compareResult)
                status = folderInfo.compareResult[nodeInfo.name]
                statusiid = self.compareWidgets.insertTreeviewItem(nodeInfo, status)
            if isinstance(nodeInfo, SyncFolderInfo):
                statusiid = self.compareWidgets.insertTreeviewItem(nodeInfo, FILE_STATUS_UNKNOWN)
                self.showStatus(nodeInfo, i)
        pass
        
    def doSync(self):
        self.fileSyncer.sync()
        self.compare()
        pass

    def doDst2SrcSync(self):
        self.fileSyncer.dst2srcSync()
        self.compare()
        pass
        
    def compare(self):
        sourceDir = self.srcFolderWidgets.folderPath.get()
        destDir = self.dstFolderWidgets.folderPath.get()
        self.srcFolderWidgets.refreshTreeview(SyncFolderInfo(sourceDir))
        self.dstFolderWidgets.refreshTreeview(SyncFolderInfo(destDir))
        

        self.config["last_source_path"] = sourceDir
        self.config["last_destination_path"] = destDir
        self.fileSyncer = FileSynchronizer(sourceDir, destDir)  
        self.fileSyncer.compare()
        
    
        self.srcFolderWidgets.clearTreeview()
        self.dstFolderWidgets.clearTreeview()
        self.compareWidgets.clearTreeview()
        self.syncShowStatus2(self.fileSyncer.srcFolderInfo, self.fileSyncer.dstFolderInfo, "", "", "All")
        self.refreshStatusShow()
        pass
    
    def syncShowStatus2(self, srcFolder, dstFolder, srcTreeParent="", dstTreeParent="", statusFilter="All"):
        if srcFolder != None:
            for f in srcFolder.nodes:
                srcFileInfo = srcFolder.nodes[f]
                srcStatus = srcFolder.compareResult[f]
                dstFileInfo = None if dstFolder == None else dstFolder.nodes.get(f, None) 
                if STATUS_STRING[srcStatus] == statusFilter or statusFilter == "All" or isinstance(srcFileInfo, SyncFolderInfo): 
                    srciid = self.srcFolderWidgets.insertTreeviewItem(srcTreeParent, srcFileInfo)
                    dstiid = self.dstFolderWidgets.insertTreeviewItem(dstTreeParent, dstFileInfo)
                    self.srcIidDstIidDict[srciid] = dstiid
                    if isinstance(srcFileInfo, FileInfo) and (self.srcFolderWidgets.inFilter(srciid) != 1) and (1 != self.dstFolderWidgets.inFilter(dstiid)):
                        self.srcFolderWidgets.filterOut(srciid)
                        self.dstFolderWidgets.filterOut(dstiid)
                if isinstance(srcFileInfo, SyncFolderInfo):
                    self.syncShowStatus2(srcFileInfo, dstFileInfo, srciid, dstiid, statusFilter)
        if dstFolder != None:
            for f in dstFolder.nodes:
                dstFileInfo = dstFolder.nodes[f]
                dstStatus = FileSynchronizer.reverseStatus(dstFolder.compareResult[f])
                srcFileInfo = None if (srcFolder == None) else srcFolder.nodes.get(f, None)
                if srcFileInfo == None:
                    if STATUS_STRING[dstStatus] == statusFilter or statusFilter == "All" or isinstance(dstFileInfo, SyncFolderInfo): 
                        srciid = self.srcFolderWidgets.insertTreeviewItem(srcTreeParent, srcFileInfo)
                        dstiid = self.dstFolderWidgets.insertTreeviewItem(dstTreeParent, dstFileInfo)
                        self.srcIidDstIidDict[srciid] = dstiid
                        if isinstance(dstFileInfo, FileInfo) and (1 != self.srcFolderWidgets.inFilter(srciid)) and (1!= self.dstFolderWidgets.inFilter(dstiid)):
                            self.srcFolderWidgets.filterOut(srciid)
                            self.dstFolderWidgets.filterOut(dstiid)
                    if isinstance(dstFileInfo, SyncFolderInfo):
                        self.syncShowStatus2(None, dstFileInfo, srciid, dstiid, statusFilter)
        srcChildrenCount = len(self.srcFolderWidgets.dirTreeview.get_children(srcTreeParent))
        dstChildrenCount = len(self.dstFolderWidgets.dirTreeview.get_children(dstTreeParent))
        if srcChildrenCount == 0 and dstChildrenCount == 0:
            self.srcFolderWidgets.dirTreeview.delete(srcTreeParent)
            self.dstFolderWidgets.dirTreeview.delete(dstTreeParent)
            try:
                del self.srcIidDstIidDict[srcTreeParent]
            except:
                pass

    
    def syncShowStatus(self, srcFolder, dstFolder, srcTreeParent="", dstTreeParent="", status="All"):
        if srcFolder != None:
            for f in srcFolder.nodes:
                srcFileInfo = srcFolder.nodes[f]
                srcStatus = srcFolder.compareResult[f]
                dstFileInfo = None if dstFolder == None else dstFolder.nodes.get(f, None) 
                # print(srcStatus)
                if STATUS_STRING[srcStatus] == status or status == "All" or isinstance(srcFileInfo, SyncFolderInfo): 
                    srciid = self.srcFolderWidgets.insertTreeviewItem(srcTreeParent, srcFileInfo)
                    dstiid = self.dstFolderWidgets.insertTreeviewItem(dstTreeParent, dstFileInfo)
                    self.srcIidDstIidDict[srciid] = dstiid
                    # statusiid = self.compareWidgets.insertTreeviewItem(srcFileInfo,srcStatus)
                if isinstance(srcFileInfo, SyncFolderInfo):
                    self.syncShowStatus(srcFileInfo, dstFileInfo, srciid, dstiid, status)
        if dstFolder != None:
            for f in dstFolder.nodes:
                dstFileInfo = dstFolder.nodes[f]
                dstStatus = FileSynchronizer.reverseStatus(dstFolder.compareResult[f])
                srcFileInfo = None if (srcFolder == None) else srcFolder.nodes.get(f, None)
                if srcFileInfo == None:
                    if STATUS_STRING[dstStatus] == status or status == "All" or isinstance(dstFileInfo, SyncFolderInfo): 
                        srciid = self.srcFolderWidgets.insertTreeviewItem(srcTreeParent, srcFileInfo)
                        dstiid = self.dstFolderWidgets.insertTreeviewItem(dstTreeParent, dstFileInfo)
                        self.srcIidDstIidDict[srciid] = dstiid
                        # statusiid = self.compareWidgets.insertTreeviewItem(dstFileInfo,dstStatus)
                    if isinstance(dstFileInfo, SyncFolderInfo):
                        self.syncShowStatus(None, dstFileInfo, srciid, dstiid, status)
        srcChildrenCount = len(self.srcFolderWidgets.dirTreeview.get_children(srcTreeParent))
        dstChildrenCount = len(self.dstFolderWidgets.dirTreeview.get_children(dstTreeParent))
        # print(srcChildrenCount,dstChildrenCount)
        if srcChildrenCount == 0 and dstChildrenCount == 0:
            self.srcFolderWidgets.dirTreeview.delete(srcTreeParent)
            self.dstFolderWidgets.dirTreeview.delete(dstTreeParent)
            try:
                del self.srcIidDstIidDict[srcTreeParent]
            except:
                pass

        pass


class Test(unittest.TestCase):

    def testName(self):
        pass


if __name__ == "__main__":
    # import sys;sys.argv = ['', 'Test.testName']
    unittest.main()
